
extern crate libc;

use libc::{close, mmap, munmap, open, perror, sleep, strcpy, O_CREAT, O_RDWR, PROT_READ, PROT_WRITE, MAP_FAILED, MAP_SHARED};
use std::ffi::{CString, NulError, c_void};
use std::ptr::null_mut;
use std::os::unix::io::RawFd;
use std::process;

struct MemoryMap {
    ptr: *mut c_void,
    length: usize,
}

impl MemoryMap {
    fn new(fd: RawFd, length: usize) -> Result<Self, &'static str> {
        let ptr = unsafe { mmap(null_mut(), length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) };
        if ptr == MAP_FAILED {
            Err("Memory mapping failed")
        } else {
            Ok(MemoryMap { ptr, length })
        }
    }

    fn write_message(&self, offset: isize, message: &CString) -> Result<(), NulError> {
        let p = unsafe { (self.ptr as *mut u8).offset(offset) };
        unsafe { strcpy(p as *mut i8, message.as_ptr()) };
        Ok(())
    }
}

impl Drop for MemoryMap {
    fn drop(&mut self) {
        unsafe {
            if munmap(self.ptr, self.length) == -1 {
                perror(CString::new("munmap").unwrap().as_ptr());
            }
            if close(self.fd) == -1 {
                perror(CString::new("close").unwrap().as_ptr());
            }
        }
    }
}

fn main() {
    let fd = unsafe { open(CString::new("temp").unwrap().as_ptr(), O_RDWR | O_CREAT, 0o664) };
    if fd == -1 {
        unsafe { perror(CString::new("open").unwrap().as_ptr()) };
        process::exit(1);
    }

    let map = match MemoryMap::new(fd, 4096) {
        Ok(map) => map,
        Err(e) => {
            eprintln!("{}", e);
            process::exit(1);
        },
    };

    let message = CString::new("haha, I'm fine").expect("CString::new failed");

    loop {
        match map.write_message(1024, &message) {
            Ok(_) => println!("message written to the shared memory~"),
            Err(_) => eprintln!("Failed to write message"),
        }
        sleep(2);
    }

    // The MemoryMap and file descriptor are automatically cleaned up here
}
